﻿using System;
using System.Collections;
using System.ComponentModel;
using System.Reflection;
using System.IO;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml.Serialization;
using System.Security;
using System.Security.Permissions;
using System.Runtime.Serialization;

using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebPartPages;
using Microsoft.SharePoint.WebPartPages.Communication;
using System.Runtime.InteropServices;

namespace SmartPart
{
    [Guid("2d7427c2-263d-45fe-acb3-4a0306093c04"), DefaultProperty("Control"), ToolboxData("<{0}:SmartPart runat=server></{0}:SmartPart>"), XmlRoot(Namespace = "SmartPart")]
    public class SmartPart : WebPart, ICellProvider, ICellConsumer
    {
        private const string defaultText = "";

        private string _userControl = defaultText;
        private Control _control = null;
        private Hashtable _userControlProperties = new Hashtable();
        private Hashtable _customProperties = new Hashtable();
        private string _userControlPath = "/UserControls";
        private bool _showUserControlList = true;

        #region GetUserControl
        internal protected virtual Control GetUserControl()
        {
            if (_control == null)
            {
                if (this.Page != null)
                {
                    if (Array.IndexOf(this.Page.Request.Form.AllKeys, this.ID + UserControlToolpart.INPUTID) > -1)
                    {
                        this.UserControl = this.Page.Request.Form[this.ID + UserControlToolpart.INPUTID];
                    }
                    if (Array.IndexOf(this.Page.Request.Form.AllKeys, this.ID + UserControlToolpart.SELECTID) > -1)
                    {
                        if (this.Page.Request.Form[this.ID + UserControlToolpart.SELECTID] != UserControlToolpart.DEFAULTLISTITEM)
                            this.UserControl = this.Page.Request.Form[this.ID + UserControlToolpart.SELECTID];
                    }


                    try
                    {
                        if (_userControl != defaultText && _userControl.IndexOf("~") != -1)
                        {
                            _control = this.Page.LoadControl(_userControl);
                        }
                        else if (_userControl != defaultText && _userControl.IndexOf("~") == -1 && _userControl.IndexOf("/") == -1 && _userControl.IndexOf(@"\") == -1)
                        {
                            // Create an latebinding instance of the given assembly name
                            String[] _userControlInput = _userControl.Split(new char[] { ',' });
                            _control = _userControlInput.Length == 1 ? (Control)LateBinder.CreateInstance(_userControl) : (Control)LateBinder.CreateInstance(_userControlInput[0], _userControlInput[1]);

                        }
                        else
                        {
                            _control = new LiteralControl(string.Format("To show a user control <a href=\"javascript:MSOTlPn_ShowToolPaneWrapper('{0}','{1}','{2}');\">open the tool pane</a> and choose one.", 1, 129, this.ID));
                        }

                        // Pass the context if the user control implements IAdvancedUserControl
                        IAdvancedUserControl containedAdvancedUserControl = _control as IAdvancedUserControl;
                        if (containedAdvancedUserControl != null)
                        {
                            containedAdvancedUserControl.SetContext(this);
                        }
                        RefreshPropertyValues();

                    }
                    catch (System.Exception ex)
                    {
                        _control = new LiteralControl(string.Format("<b>Error:</b> unable to load {0}<br /><b>Details:</b> {1}", _userControl, ex.Message));
                    }
                }
            }

            return _control;
        }
        #endregion

        #region Properties
        [Browsable(false),
        DefaultValue(defaultText),
        WebPartStorage(Storage.Personal)]
        public string UserControl
        {
            get
            {
                return _userControl;
            }

            set
            {
                _userControl = value;
            }
        }

        [Browsable(false),
        DefaultValue(true),
        WebPartStorage(Storage.Personal)]
        public bool ShowUserControlList
        {
            get
            {
                return _showUserControlList;
            }

            set
            {
                _showUserControlList = value;
            }
        }

        [Browsable(false),
        DefaultValue(""),
        WebPartStorage(Storage.Personal)]
        public string SerializedProperties
        {
            get
            {
                try
                {
                    MemoryStream ms = new MemoryStream();
                    System.Runtime.Serialization.Formatters.Soap.SoapFormatter sf =
                new System.Runtime.Serialization.Formatters.Soap.SoapFormatter();
                    sf.Serialize(ms, _userControlProperties);

                    return Encoding.Default.GetString(ms.ToArray());
                }
                catch
                {
                    return null;
                }
            }
            set
            {
                try
                {
                    MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(value));

                    System.Runtime.Serialization.Formatters.Soap.SoapFormatter sf =
                new System.Runtime.Serialization.Formatters.Soap.SoapFormatter();
                    _userControlProperties = (Hashtable)sf.Deserialize(ms);
                }
                catch
                {
                    _userControlProperties = new Hashtable();
                }
            }
        }

        /// <summary>
        /// Hashtable containing the property values for the contained control
        /// </summary>
        [Browsable(false)]
        internal protected Hashtable UserControlProperties
        {
            get
            {
                return (this._userControlProperties);
            }
            set
            {
                this._userControlProperties = value;
            }
        }

        [Browsable(false),
        DefaultValue("UserControls"),
        WebPartStorage(Storage.Personal)]
        public string UserControlPath
        {
            get
            {
                return _userControlPath;
            }
            set
            {
                _userControlPath = value;
            }
        }
        #endregion

        #region CustomProperties
        /// <summary>
        /// Gets or sets whether a Web Part's properties have changed and should be persisted at the end of page rendering.
        /// </summary>
        [WebPartStorage(Storage.None)]
        public bool SaveWebPartProperties
        {
            get
            {
                return this.SaveProperties;
            }
            set
            {
                this.SaveProperties = true;
            }
        }

        public virtual void SetCustomProperty(string propertyName, object propertyValue)
        {
            if (_customProperties == null)
                _customProperties = new Hashtable();

            if (_customProperties.ContainsKey(propertyName))
                if (propertyValue != null)
                    _customProperties[propertyName] = propertyValue;
                else
                    _customProperties.Remove(propertyName);
            else
                if (propertyValue != null)
                    _customProperties.Add(propertyName, propertyValue);

            this.SaveProperties = true;
        }

        public virtual object GetCustomProperty(string propertyName)
        {
            if (_customProperties != null)
            {
                if (_customProperties.ContainsKey(propertyName))
                    return _customProperties[propertyName];
                else
                    return null;
            }
            else
                return null;
        }

        [Browsable(false),
        DefaultValue(""),
        WebPartStorage(Storage.Personal)]
        public string SerializedCustomProperties
        {
            get
            {
                try
                {
                    MemoryStream ms = new MemoryStream();
                    System.Runtime.Serialization.Formatters.Soap.SoapFormatter sf =
                        new System.Runtime.Serialization.Formatters.Soap.SoapFormatter();
                    sf.Serialize(ms, _customProperties);

                    return Encoding.UTF8.GetString(ms.ToArray());
                }
                catch
                {
                    return null;
                }
            }
            set
            {
                try
                {
                    MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(value));

                    System.Runtime.Serialization.Formatters.Soap.SoapFormatter sf =
                        new System.Runtime.Serialization.Formatters.Soap.SoapFormatter();

                    _customProperties = (Hashtable)sf.Deserialize(ms);
                }
                catch
                {
                    _customProperties = new Hashtable();
                }
            }
        }
        #endregion

        #region GetToolParts
        /// <summary>
        ///	This method gets the custom tool parts for this Web Part by overriding the
        ///	GetToolParts method of the WebPart base class. You must implement
        ///	custom tool parts in a separate class that derives from 
        ///	Microsoft.SharePoint.WebPartPages.ToolPart. 
        ///	</summary>
        ///<returns>An array of references to ToolPart objects.</returns>
        public override ToolPart[] GetToolParts()
        {
            System.Collections.ArrayList al = new ArrayList();

            WebPartToolPart wptp = new WebPartToolPart();
            CustomPropertyToolPart custom = new CustomPropertyToolPart();

            if (this.ShowUserControlList)
                al.Add(new UserControlToolpart(UserControlPath));

            // Check for custom toolparts
            IAdvancedUserControl containedAdvancedUserControl = GetUserControl() as IAdvancedUserControl;
            if (containedAdvancedUserControl != null)
            {
                if (containedAdvancedUserControl.GetCustomToolParts() != null)
                    al.AddRange(containedAdvancedUserControl.GetCustomToolParts());
            }

            al.Add(custom);
            al.Add(wptp);

            if (_control == null)
                GetUserControl();
            if (_control != null)
            {
                if (_control.GetType() != typeof(LiteralControl))
                {
                    UserControlPropertiesToolpart ucToolPart = new UserControlPropertiesToolpart(_control);
                    if (ucToolPart.NumberOfProperties > 0)
                        al.Add(ucToolPart);
                }
            }
            al.Add(new AboutToolpart());

            wptp.Expand(Microsoft.SharePoint.WebPartPages.WebPartToolPart.Categories.Appearance);
            custom.Expand("User Control");
            return (ToolPart[])al.ToArray(typeof(ToolPart));
        }
        #endregion

        public virtual void RefreshPropertyValues()
        {
            if (_control != null)
            {
                Type controlType = _control.GetType();

                foreach (DictionaryEntry de in _userControlProperties)
                {
                    try
                    {
                        PropertyInfo pi = controlType.GetProperty(de.Key.ToString());
                        pi.SetValue(_control, de.Value, null);
                    }
                    catch { } // ignore errors
                }
            }
        }

        /// <summary>
        /// Load the UserControl
        /// </summary>
        protected override void CreateChildControls()
        {
            base.CreateChildControls();
            this.Controls.Add(GetUserControl());
        }


        /// <summary>
        /// Render this Web Part to the output parameter specified.
        /// </summary>
        /// <param name="output"> The HTML writer to write out to </param>
        protected override void RenderWebPart(HtmlTextWriter output)
        {
            EnsureChildControls();
            if (GetUserControl() != null)
            {
                GetUserControl().RenderControl(output);
            }
        }

        #region Connectable stuff

        private string cellName = "Smart Part Cell";

        public event Microsoft.SharePoint.WebPartPages.Communication.CellReadyEventHandler CellReady;

        public event Microsoft.SharePoint.WebPartPages.Communication.CellProviderInitEventHandler CellProviderInit;

        void ICellProvider.CellConsumerInit(object sender, CellConsumerInitEventArgs cellConsumerInitEventArgs)
        {
            // TODO:  Add UserControlWebpart.CellConsumerInit implementation
        }

        public override void EnsureInterfaces()
        {
            try
            {
                //Check if UserControl has the IProviderInterface
                if (GetUserControl() != null)
                {
                    if (GetUserControl().GetType().GetInterface(typeof(IConnectionProviderControl).Name) != null)
                    {
                        // Register the ICellProvider interface
                        RegisterInterface("CellProvider_WPQ_",
                            "ICellProvider",
                            WebPart.UnlimitedConnections,
                            ConnectionRunAt.Server,
                            this,
                            "CellProvider_WPQ_",
                            ((IConnectionProviderControl)GetUserControl()).ProviderMenuLabel,
                            ((IConnectionProviderControl)GetUserControl()).ProviderMenuLabel);
                    }

                    if (GetUserControl().GetType().GetInterface(typeof(IConnectionConsumerControl).Name) != null)
                    {
                        // Register the ICellConsumer interface.
                        RegisterInterface("CellConsumer_WPQ_",
                            "ICellConsumer",
                            WebPart.UnlimitedConnections,
                            ConnectionRunAt.Server,
                            this,
                            "CellConsumer_WPQ_",
                            ((IConnectionConsumerControl)GetUserControl()).ConsumerMenuLabel,
                            ((IConnectionConsumerControl)GetUserControl()).ConsumerMenuLabel);
                    }
                }
            }
            catch (SecurityException)
            {
            }
        }

        public override ConnectionRunAt CanRunAt()
        {
            return ConnectionRunAt.Server;
        }

        public override void PartCommunicationConnect(string interfaceName,
            WebPart connectedPart,
            string connectedInterfaceName,
            ConnectionRunAt runAt)
        {
            EnsureChildControls();

            if (interfaceName == "CellConsumer_WPQ_")
            {
                cellConnectedCount++;
            }
        }

        public override void PartCommunicationInit()
        {
            if (CellProviderInit != null)
            {
                CellProviderInitEventArgs cellProviderInitArgs = new CellProviderInitEventArgs();
                cellProviderInitArgs.FieldName = cellName;
                CellProviderInit(this, cellProviderInitArgs);
            }

            if (cellConnectedCount > 0)
            {
                if (CellConsumerInit != null)
                {
                    CellConsumerInitEventArgs cellConsumerInitArgs = new CellConsumerInitEventArgs();
                    cellConsumerInitArgs.FieldName = cellName;
                    CellConsumerInit(this, cellConsumerInitArgs);
                }
            }
        }

        public override void PartCommunicationMain()
        {
            if (GetUserControl().GetType().GetInterface(typeof(IConnectionProviderControl).Name) != null)
                if (CellReady != null)
                {
                    CellReadyEventArgs cellReadyArgs = new CellReadyEventArgs();
                    cellReadyArgs.Cell = ((IConnectionProviderControl)GetUserControl()).GetProviderData();
                    CellReady(this, cellReadyArgs);
                }
        }

        // Keep a count of ICellConsumer connections.
        private int cellConnectedCount = 0;

        public event CellConsumerInitEventHandler CellConsumerInit;

        public override InitEventArgs GetInitEventArgs(string interfaceName)
        {
            if (interfaceName == "CellConsumer_WPQ_")
            {
                EnsureChildControls();
                CellConsumerInitEventArgs cellConsumerInitArgs = new CellConsumerInitEventArgs();
                cellConsumerInitArgs.FieldName = cellName;
                return (cellConsumerInitArgs);
            }
            else
            {
                return (null);
            }
        }
        void ICellConsumer.CellProviderInit(object sender, CellProviderInitEventArgs cellProviderInitArgs)
        {
            // This is where the Consumer part can identify what type of "Cell" the Provider
            // will be sending.
        }
        void ICellConsumer.CellReady(object sender, CellReadyEventArgs cellReadyArgs)
        {
            if (cellReadyArgs.Cell != null)
            {
                ((IConnectionConsumerControl)GetUserControl()).SetConsumerData(cellReadyArgs.Cell);
            }
        }
        #endregion
    }
}